using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using System.Threading;
using System.Threading.Tasks;

namespace Comteck.Winforms.Infrastructure {
  /// <summary>
  /// 语音
  /// </summary>
  public class SpeechHelper : BaseLog, IDisposable {

    #region fields

    /// <summary>
    /// 应用程序线程
    /// </summary>
    public System.Threading.SynchronizationContext SynchronizationContext { get; set; }

    /// <summary>
    /// 说队列
    /// </summary>
    private ConcurrentQueue<string> speakQueue = null;

    /// <summary>
    /// 读分析
    /// </summary>
    private SpeechSynthesizer speaker { get; set; }

    /// <summary>
    /// 识别引擎
    /// </summary>
    private SpeechRecognitionEngine recognizer { get; set; }

    /// <summary>
    /// 是否启用语音播报
    /// </summary>
    private Func<bool> enable_speech;

    /// <summary>
    /// 是否启用语音指令
    /// </summary>
    private Func<bool> enable_speechcmd;

    /// <summary>
    /// 解析事件
    /// </summary>
    public event EventHandler SpeechRecongized;

    #endregion

    #region 线程相关

    Thread thread = null;

    private ManualResetEvent mreExit = new ManualResetEvent(false);

    int WAIT_COUNT = 500;

    #endregion

    #region ctor

    public SpeechHelper() {
    }

    #endregion

    #region 初始化

    /// <summary>
    /// 初始化语音识别
    /// </summary>
    public void Init(IList<string> cmds
        , Func<bool> enable_speech
        , Func<bool> enable_speechcmd
        , System.Threading.SynchronizationContext synchronizationContext) {
      this.enable_speech = enable_speech;
      this.enable_speechcmd = enable_speechcmd;
      this.SynchronizationContext = synchronizationContext;

      try {
        isrun = true;
        mreExit.Reset();

        //语音播报
        if (this.enable_speech?.Invoke() ?? false) {
          speakQueue = new ConcurrentQueue<string>();
          speaker = new SpeechSynthesizer();
          speaker.SelectVoiceByHints(VoiceGender.Female);

          thread = new Thread(SpeekWorker) {
            IsBackground = true
          };
          thread.Start();

          Logger.Info($"语音播报开始工作:{thread?.Name}{ thread?.ThreadState}");
          Speek("初始化语音播报成功！");
        } else {
          this.Dispose();
        }

//#if DEBUG
//        //语音指令
//        if ((this.enable_speechcmd?.Invoke() ?? false)) {

//          recognizer = new SpeechRecognitionEngine(
//          new System.Globalization.CultureInfo("zh-CN")
//          );

//          Choices commands = new Choices();
//          commands.Add(cmds.ToArray());

//          GrammarBuilder g_builder = new GrammarBuilder();
//          g_builder.Append(commands);

//          Grammar grammer = new Grammar(g_builder);

//          recognizer.LoadGrammarAsync(grammer);

//          recognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(Recognizer_SpeechRecongized);

//          recognizer.SetInputToDefaultAudioDevice();

//          recognizer.RecognizeAsync(RecognizeMode.Multiple);

//          Speek("初始化语音识别成功！");
//        }
//#endif

      } catch (Exception ex) {
        Logger.Error($"初始化语音异常 {ex.StackTrace}");
      }
    }

    #endregion

    #region 解析语音命令

    private int recongizedLock = 0;

    void Recognizer_SpeechRecongized(object sender, SpeechRecognizedEventArgs e) {
      Logger.Info($"语音命令 {e.Result.Text}");

      if (Interlocked.CompareExchange(ref recongizedLock, 1, 0) == 1) {
        return;
      }

      new TaskFactory().StartNew(new Action(() =>
      {
        this.SynchronizationContext.Send((obj) => {
          SpeechRecongized?.Invoke(e.Result.Text, e);
        }, null);

        Interlocked.Exchange(ref recongizedLock, 0);
      }));
    }

    #endregion

    #region 语音播报

    bool isrun = false;

    /// <summary>
    /// 语音工作
    /// </summary>
    /// <param name="t"></param>
    private void SpeekWorker() {
      while (isrun) {
        //等待
        if (mreExit.WaitOne(WAIT_COUNT)) {
          break;
        }

        try {
          var tmpstr = string.Empty;

          if (speakQueue.TryDequeue(out tmpstr)) {
            speaker.Speak(tmpstr);
          }
        } catch (Exception ex) {
          Logger.Error($"语音播报线程[work]异常:{ex.StackTrace}");
          continue;
        }
      }
    }

    /// <summary>
    /// 加入队列
    /// </summary>
    /// <param name="content"></param>
    public void Speek(string content) {
      if (string.IsNullOrWhiteSpace(content)) {
        return;
      }

      speakQueue?.Enqueue(content);
    }

    #endregion

    #region disposed

    public void Dispose() {
      mreExit?.Set();
      isrun = false;

      speaker?.Dispose();
      recognizer?.Dispose();
    }

    #endregion

  }
}
